package com.decent.manager.config.security.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.decent.common.bo.MessageBean;
import com.decent.common.entity.manager.SystemUser;
import com.decent.common.enums.AuthExceptionEnum;
import com.decent.common.enums.ErrorCodeEnum;
import com.decent.common.util.http.GetIpUtil;
import com.decent.manager.system.auth.jwt.JwtPayLoad;
import com.decent.manager.system.auth.jwt.JwtTokenUtil;
import com.decent.manager.system.auth.service.AuthService;
import com.decent.manager.system.auth.vo.LoginUser;
import com.decent.manager.system.service.SystemUserService;
import com.decent.manager.system.util.CommonServletUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * @author 张子旭
 * @date 2022/1/29
 */
@Slf4j
public class JwtLoginFilter extends UsernamePasswordAuthenticationFilter {
    private final SystemUserService systemUserService;
    private final AuthService authService;

    public JwtLoginFilter(AuthenticationManager authenticationManager, SystemUserService systemUserService, AuthService authService) {
        super.setUsernameParameter("account");
        super.setAuthenticationManager(authenticationManager);
        super.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/**/auth/login"));
        this.systemUserService = systemUserService;
        this.authService = authService;
    }

    @SneakyThrows
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        SystemUser user;
        String userAccount = request.getParameter("userAccount");
        String loginPassword = request.getParameter("loginPassword");
        // form表单登录
        if (!StrUtil.isAllBlank(userAccount, loginPassword)) {
            user = SystemUser.builder().userAccount(userAccount).loginPassword(loginPassword).build();
        }
        // json流登录
        else {
            String body;
            try (BufferedReader br = request.getReader()) {
                StringBuilder paramSb = new StringBuilder();
                String str;
                while ((str = br.readLine()) != null) {
                    paramSb.append(str);
                }
                body = paramSb.toString();
            }
            if (StrUtil.isBlank(body)){
                CommonServletUtil.writeJsonToResponse(response, new MessageBean(AuthExceptionEnum.LOGIN_EXPIRED));
                return null;
            }
            user = JSONUtil.toBean(body, SystemUser.class);
        }
        if (Objects.nonNull(user)) {
            return super.getAuthenticationManager()
                    .authenticate(new UsernamePasswordAuthenticationToken(user.getUserAccount(), user.getLoginPassword()));
        }
        CommonServletUtil.writeJsonToResponse(response, new MessageBean(AuthExceptionEnum.LOGIN_EXPIRED));
        return null;
    }

    @Override
    public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) {
        LoginUser principal = (LoginUser) authResult.getPrincipal();
        //生成token
        JwtPayLoad jwtPayLoad = new JwtPayLoad(principal.getId(), principal.getUserAccount());
        String token = JwtTokenUtil.sign(jwtPayLoad);
        // 缓存token与登录用户信息对应, 默认2个小时
        //设置最后登录ip和时间
        principal.setLastLoginIp(GetIpUtil.getIp());
        principal.setLastLoginTime(LocalDateTime.now());
        //更新用户登录信息
        systemUserService.updateUserById(principal);
        // 获取用户权限信息
        principal = authService.genSysLoginUser(principal);
        authService.setSpringSecurityContextAuthentication(principal);
        // redis缓存用户
        authService.cacheLoginUser(jwtPayLoad, principal);
        CommonServletUtil.writeJsonToResponse(response, MessageBean.success(token, "登陆成功"));
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
        MessageBean message = new MessageBean();
        message.setCode(ErrorCodeEnum.NO.getCode());
        if (failed instanceof UsernameNotFoundException || failed instanceof BadCredentialsException) {
            message.setMessage(AuthExceptionEnum.ACCOUNT_PWD_ERROR.getMessage());
        } else if (failed instanceof AccountStatusException) {
            message.setMessage(AuthExceptionEnum.ACCOUNT_FREEZE_ERROR.getMessage());
        }
        log.warn("[{}]授权异常[{}]", request.getRequestURI(), failed.getLocalizedMessage());
        CommonServletUtil.writeJsonToResponse(response, message);
    }
}
